//
// Copyright (c) 2009 Tridium, Inc.
// Licensed under the Academic Free License version 3.0
//
// History:
//   24 Feb 09  Dan Giorgis  Creation
//

#include "sedona.h"
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h> 
#include <unistd.h> 
#include <time.h>
#include <stdio.h>

#ifdef _WIN32
extern long _timezone;
extern int _daylight;
#else
extern long timezone;
extern int daylight;
#endif

/* Mini hwclock implementation for busybox 
  * 
  * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by 
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version. 
  * 
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
  * General Public License for more details. 
  * 
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  *
  */
  
/* Copied from linux/rtc.h to eliminate the kernel dependency */
struct linux_rtc_time {	
    int tm_sec;
    int tm_min;	
    int tm_hour;	
    int tm_mday;	
    int tm_mon;	
    int tm_year;	
    int tm_wday;	
    int tm_yday;	
    int tm_isdst;
};  
 
struct sysinfo {  
    long uptime;             /* Seconds since boot */   
    unsigned long loads[3];  /* 1, 5, and 15 minute load averages */   
    unsigned long totalram;  /* Total usable main memory size */
    unsigned long freeram;   /* Available memory size */   
    unsigned long sharedram; /* Amount of shared memory */   
    unsigned long bufferram; /* Memory used by buffers */   
    unsigned long totalswap; /* Total swap space size */   
    unsigned long freeswap;  /* swap space still available */    
    unsigned short procs;    /* Number of current processes */   
    unsigned long totalhigh; /* Total high memory size */  
    unsigned long freehigh;  /* Available high memory size */  
    unsigned int mem_unit;   /* Memory unit size in bytes */    
    char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding for libc5 */
}; 

#define RTC_SET_TIME   _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time    */
#define RTC_RD_TIME    _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time   */

#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#endif

static void Error(const char *Msg)
{	
	fprintf (stderr, "%s\n", Msg);	
	//fprintf (stderr, "strerror() is %s\n", strerror(errno));	 
}

static time_t read_rtc(int utc)
{
	int rtc;
	struct tm tm;
	char *oldtz = 0;
	time_t t = 0;

	if (( rtc = open ( "/dev/rtc", O_RDONLY )) < 0 ) {
		if (( rtc = open ( "/dev/misc/rtc", O_RDONLY )) < 0 ) {
			Error ( "Could not access RTC" );
                    return -1;
		}
	}
 	memset ( &tm, 0, sizeof( struct tm ));
	if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 ) {
		Error ( "Could not read time from RTC" );
            return -1;
	}
    
	tm. tm_isdst = -1; // not known

	close ( rtc );

	if ( utc ) {
		oldtz = getenv ( "TZ" );
		setenv ( "TZ", "UTC 0", 1 );
		tzset ( );
	}

	t = mktime ( &tm );

	if ( utc ) {
		if ( oldtz )
			setenv ( "TZ", oldtz, 1 );
		else
			unsetenv ( "TZ" );
		tzset ( );
	}
    
	return t;
}


static void write_rtc(time_t t, int utc)
{
	int rtc;
	struct tm tm;

	if (( rtc = open ( "/dev/rtc", O_WRONLY )) < 0 ) {
		if (( rtc = open ( "/dev/misc/rtc", O_WRONLY )) < 0 ) {
			Error ( "Could not access RTC" );
                    return;
		}
	}

	tm = *( utc ? gmtime ( &t ) : localtime ( &t ));
	tm. tm_isdst = 0;

	if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 ) 
		Error ( "Could not set the RTC time" );

	close ( rtc );
}

static int to_sys_clock(int utc)
{
	struct timeval tv = { 0, 0 };
	const struct timezone tz = { timezone/60 - 60*daylight, 0 };

	tv. tv_sec = read_rtc ( utc );

	if ( settimeofday ( &tv, &tz ))
		Error ( "settimeofday() failed" );

	return 0;
}

static int from_sys_clock(int utc)
{
	struct timeval tv = { 0, 0 };
	struct timezone tz = { 0, 0 };

	if ( gettimeofday ( &tv, &tz )) {
		Error ( "gettimeofday() failed" );
	}

	write_rtc ( tv. tv_sec, utc );
	return 0;
}

//  Difference in seconds between ANSI C Epoch of midnight Jan 1 1970 and
//  the Sedona epoch of midnight Jan 1 2000.  There were 7 leap years
//  in this timeframe - 72,76,80,84,88,92,96

#define SEDONA_EPOCH_OFFSET_SECS ((int64_t)(((365L * 30L) + 7L) * 24L * 60L * 60L))

////////////////////////////////////////////////////////////////
// Native Methods
////////////////////////////////////////////////////////////////

// long doNow()
int64_t datetimeStd_DateTimeServiceStd_doNow(SedonaVM* vm, Cell* params)
{
  time_t now = time(NULL);
  int64_t nanos;
  now -= SEDONA_EPOCH_OFFSET_SECS;
  nanos = ((int64_t) now * 1000 * 1000 *1000);
  return nanos;
}

 //  Setting system time not implemented on Win32
  
// void doSetClock 
Cell datetimeStd_DateTimeServiceStd_doSetClock(SedonaVM* vm, Cell* params)
{
  int64_t nanos = *(int64_t*)(params+0); // param 0+1
    
  return nullCell;
}

Cell datetimeStd_DateTimeServiceStd_doGetUtcOffset(SedonaVM* vm, Cell* params)
{
  Cell result;
   
  //  Per Microsoft CRT docs, timezone global variable is updated whenever 
  //  localtime is called.  QNX follows same convention.
  //  timezone is difference between localtime and GMT, so we must negate to 
  //  get utcOffset as we define it.
  time_t now = time(NULL);
  struct tm *lcltime = localtime(&now);
  
  #ifdef _WIN32
    result.ival = -(int)_timezone;
    if (_daylight)
      result.ival += 3600;  //  if daylight savings active, advance one hour
  #else
    result.ival = -(int)timezone;
    if (daylight)
      result.ival += 3600;  //  if daylight savings active, advance one hour
  #endif

//printf("timezone %lld result.ival %d\n", _timezone, result.ival);

  return result;
  
}  

Cell datetimeStd_DateTimeServiceStd_doSetHWClock(SedonaVM* vm, Cell* params)
{
     
     time_t seconds  = (time_t)params[0].ival;
     time_t rawtime;
     struct timeval tv;  
     struct tm * timeinfo;
     
     printf("seconds is %d\n", (int)seconds);
     
     tv.tv_sec = seconds;	
     tv.tv_usec = 0;

    if(settimeofday (&tv, (struct timezone *) 0) < 0)	  {
        printf("Set system datatime error!\n"); 
    } else {   
        printf("Set system datatime successfully!\n");
    }


    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
    printf ("The current date/time is: %s", asctime (timeinfo)); 

    printf ("Set system time to RTC\n\n"); 
    
    from_sys_clock(0);
 
    return zeroCell;          
}

Cell datetimeStd_DateTimeServiceStd_doGetSysUptime(SedonaVM* vm, Cell* params)
{
    Cell  ret;
    struct sysinfo info;
    int val;

    val = sysinfo(&info);
    if (val !=0) {
        ret.ival = -1;
        return ret;
    }

    ret.ival = (int32_t)info.uptime;
    return ret;
}
    

Cell datetimeStd_DateTimeServiceStd_doGetSystime(SedonaVM* vm, Cell* params)
{
     Cell     ret; 

     ret.ival = (int)time(NULL);

     return ret;
}

int getDateString(char *httpHeader, char *dateStr)
{
	char * p1 = strstr(httpHeader, "Date");
	if (p1 == NULL) {
		return -1;
	}
	
	char *p2 = strstr(httpHeader, "GMT");
	if (p2 == NULL) {
		return -1;
	}
	
	//Date: Thu, 19 Sep 2013 03:38:20 GMT
	int len  = (p2 - 2) - (p1 + 6) + 1;
	strncpy(dateStr, p1+6, len);
	dateStr[len] = '\0';
	printf("The time String is %s\n", dateStr);
	
	return 0;
}

int convertDateStringToSec(char fmt[],char dateStr[])
{
	int sec = 0;
	struct tm tb;

	if (strptime(dateStr, fmt, &tb) == 0) {
		printf("[convertDateStringToSec]format the time to seconds failed !\n");
		return -1;
	}	
	
	sec = mktime(&tb);

	return sec;
}


Cell datetimeStd_DateTimeServiceStd_parseTimeFromHttp(SedonaVM* vm, Cell* params)
{
	 Cell	  ret; 
	 char  dateStr[50];
	 char* document;
	 char *fmt = "%a, %d %b %C%y %H:%M:%S";

	 memset(dateStr, 0, sizeof(char)*50);
	 document  = params[1].aval;
	 if (document == NULL) {
		return negOneCell;
	 }
	 
	 ret.ival = getDateString(document, dateStr);
	 if (ret.ival == -1) {
		return ret;
	 }
	 
	 ret.ival = convertDateStringToSec(fmt, dateStr);
	 
	 return ret;
}


